package com.bestcem.xm.survey.service;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bestcem.xm.qdes.grpc.v1.services.Question;
import com.bestcem.xm.survey.controller.vo.SurveyColumnVo;
import com.bestcem.xm.survey.controller.vo.SurveyQuotaCheckItemVo;
import com.bestcem.xm.survey.entity.SurveyRespondent;
import com.bestcem.xm.survey.entity.SurveyRespondentData;
import com.bestcem.xm.survey.grpc.client.dto.ProjectDto;
import com.bestcem.xm.survey.grpc.client.dto.QstructsDto;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.javatuples.Pair;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

import static com.bestcem.xm.deliver.grpc.v1.services.DeliverExtparamsExtparamType.DELIVEREXTPARAMS_EXTPARAM_TYPE_DATA_ENUM_VALUE;

/**
 * 公用的代码写这里
 */
public interface SurveyCommonService {

    ConcurrentHashMap<Integer, Function<String, Object>> VALUE_FORMATE_FUNC = new ConcurrentHashMap<Integer, Function<String, Object>>() {
        {
            put(0, Integer::parseInt);
            put(1, String::valueOf);
            put(2, Float::parseFloat);
            put(3, Float::parseFloat);
            put(4, s -> {
                if (Lists.newArrayList("TRUE", "True", "true", "1").contains(s)) {
                    return Boolean.TRUE;
                } else {
                    return Boolean.FALSE;
                }
            });
            put(5, String::valueOf);
        }
    };

    default <T> boolean checkNull(T t, Predicate<T> predicate) {
        if (t == null) {
            return false;
        }
        return predicate.test(t);
    }

    default boolean doCheckClientTypeQuotafull(int client_type, String project_id) {
        //TODO python 也没实现设备配额校验
        return false;
    }

    Boolean testRetryable(Integer code) throws Exception;

    default JSONObject formationExtparams(String name, Object value, Integer etype, String code) {
        JSONObject ret = new JSONObject();
        JSONObject retson = new JSONObject();
        if (etype.equals(DELIVEREXTPARAMS_EXTPARAM_TYPE_DATA_ENUM_VALUE)) {
            retson.put("code", code);
            retson.put("desc", value);
            retson.put("type", etype);
        } else {
            retson.put("code", code);
            retson.put("type", etype);
        }
        ret.put(name, retson);
        return ret;
    }

    default Object extparamsTransfer(JSONObject deliver_extparams, String value) {
        Integer dtype = deliver_extparams.getInteger("extparam_type") == null ? deliver_extparams.getInteger("dtype") : deliver_extparams.getInteger("extparam_type");
        return VALUE_FORMATE_FUNC.get(dtype).apply(value);
    }

    default Map<String, LocalDateTime> calcTimeRange4CheckCunt(String f_type) {
        Map<String, LocalDateTime> map = new HashMap<>();
        //日期+时分秒
        LocalDateTime current_datetime = LocalDateTime.now();
        //日期
        LocalDate current_date = current_datetime.toLocalDate();
        //获取年，月，日，时，
        int year = current_datetime.get(ChronoField.YEAR);
        int month = current_datetime.get(ChronoField.MONTH_OF_YEAR);
        int day = current_datetime.get(ChronoField.DAY_OF_MONTH);
        int hour = current_datetime.get(ChronoField.HOUR_OF_DAY);
        //当日是本周第几天，python weekday周五返回4，java返回5
        int week = current_datetime.get(ChronoField.DAY_OF_WEEK);

        LocalDateTime gt_time;
        LocalDateTime lt_time;
        if ("hour".equals(f_type)) {
            //时分秒，没有日期
            LocalTime localTime = LocalTime.of(hour, 0);
            gt_time = LocalDateTime.of(current_date, localTime);
            lt_time = gt_time.plus(1, ChronoUnit.HOURS);
        } else if ("day".equals(f_type)) {
            gt_time = LocalDateTime.of(year, month, day, 0, 0);
            lt_time = gt_time.plus(1, ChronoUnit.DAYS);
        } else if ("week".equals(f_type)) {
            gt_time = LocalDateTime.of(year, month, day, 0, 0).minus(week - 1, ChronoUnit.DAYS);
            lt_time = gt_time.plus(7, ChronoUnit.DAYS);
        } else if ("month".equals(f_type)) {
            gt_time = LocalDateTime.of(year, month, 1, 0, 0);
            lt_time = gt_time.plus(1, ChronoUnit.MONTHS);
        } else if ("quarter".equals(f_type)) {
            int quarterMonthStart = (month - 1) - (month - 1) % 3 + 1;
            gt_time = LocalDateTime.of(year, quarterMonthStart, 1, 0, 0);
            lt_time = gt_time.plus(3, ChronoUnit.MONTHS);
        } else if ("year".equals(f_type)) {
            gt_time = LocalDateTime.of(year, 1, 1, 0, 0);
            lt_time = gt_time.plus(1, ChronoUnit.YEARS);
        } else if ("7_days".equals(f_type)) {
            gt_time = current_datetime.minus(7, ChronoUnit.DAYS);
            lt_time = current_datetime;
        } else if ("30_days".equals(f_type)) {
            gt_time = current_datetime.minus(30, ChronoUnit.DAYS);
            lt_time = current_datetime;
        } else if ("90_days".equals(f_type)) {
            gt_time = current_datetime.minus(90, ChronoUnit.DAYS);
            lt_time = current_datetime;
        } else if ("365_days".equals(f_type)) {
            gt_time = current_datetime.minus(365, ChronoUnit.DAYS);
            lt_time = current_datetime;
        } else {
            return map;
        }
        map.put("gt_time", gt_time);
        map.put("lt_time", lt_time);
        return map;
    }

    default JSONObject parseProject(JSONObject data, JSONObject attr) {
        if (attr.getBooleanValue("openGrouping") && data.containsKey("groups")) {
            JSONObject q_dict = new JSONObject();
            for (JSONObject question : walkQstructQuestions(data)) {
                q_dict.put(question.getString("gid"), question);
            }
            JSONObject groups = data.getJSONObject("groups");
            data.remove("groups");

            JSONObject root_grp = fillProjectGroups(groups.getJSONObject("0").getJSONArray("data").getJSONObject(0), groups, q_dict);
            if (root_grp != null && !root_grp.isEmpty()) {
                data.putAll(root_grp);
            }
            data.put("one_question_on_page", true);
            data.put("openGrouping", true);
        }

        data.put("gid", 0);
        return data;
    }

    default JSONObject fillProjectGroups(JSONObject group, JSONObject group_dict, JSONObject q_dict) {
        String gid = group.getString("gid");
        JSONObject pgroup = group_dict.getJSONObject(gid);
        if (pgroup.isEmpty()) {
            return null;
        }
        JSONObject data;
        Object data_tmp = pgroup.get("data");
        if (data_tmp instanceof List) {
            JSONArray array = (JSONArray) data_tmp;
            data = new JSONObject();
            for (int i = 0; i < array.size(); i++) {
                JSONObject d = array.getJSONObject(i);
                data.put(d.getString("gid"), d);
            }
        } else {
            data = (JSONObject) data_tmp;
        }

        JSONArray items = new JSONArray();
        JSONArray sorts = pgroup.getJSONArray("sorts");
        for (int i = 0; i < sorts.size(); i++) {
            JSONObject d = sorts.getJSONObject(i);
            String type = d.getString("type");
            String forgid = d.getString("gid");
            if (StrUtil.isNotBlank(forgid) && Integer.parseInt(forgid) > 0) {
                if ("G".equals(type)) {
                    JSONObject sub_grp = data.getJSONObject(gid);
                    if (sub_grp.isEmpty()) {
                        continue;
                    }
                    sub_grp = fillProjectGroups(sub_grp, group_dict, q_dict);
                    if (!sub_grp.isEmpty()) {
                        items.add(sub_grp);
                    }
                } else if ("Q".equals(type)) {
                    JSONObject q = q_dict.getJSONObject(gid);
                    if (!q.isEmpty()) {
                        items.add(q);
                    }
                }
            }
        }
        if (!items.isEmpty()) {
            group.put("items", items);
        }
        return group;
    }

    default List<JSONObject> walkQstructQuestions(JSONObject data) {
        List<JSONObject> questionList = new ArrayList<>();
        if (data == null) {
            return questionList;
        }
        String itemsStr = data.getString("items");
        if (StringUtils.isEmpty(itemsStr)) {
            return questionList;
        }
        List<String> items = JSONArray.parseArray(itemsStr, String.class);
        for (String item : items) {
            if (NumberUtils.isDigits(item)) {
                continue;
            }
            JSONObject itemJs = JSONObject.parseObject(item);
            String type = itemJs.getString("type");
            if ("Q".equals(type)) {
                if (itemJs.getString("items") != null) {
                    itemJs.put("items", JSONArray.parseArray(itemJs.getString("items")));
                }
                questionList.add(itemJs);
            } else if ("G".equals(type)) {
                questionList.addAll(walkQstructQuestions(itemJs));
            }
        }
        return questionList;
    }

    /**
     * 对应strip_data
     */
    default Object removeSeq(Object jsonData) {
        //String data = JSON.toJSONString(jsonData);
        //if (JSONUtil.isTypeJSONObject(data)) {
        //    JSONObject itemobj = new JSONObject();
        //    JSONObject handlerData = JSONObject.parseObject(data);
        //    handlerData.forEach((k, v) -> itemobj.put(k, removeSeq(v)));
        //    String type = itemobj.getString("type");
        //    if (StrUtil.isNotEmpty(type) && ("O".equals(type) || "R".equals(type))) {
        //        itemobj.remove("seq");
        //    }
        //    return itemobj;
        //} else if (JSONUtil.isTypeJSONArray(data)) {
        //    JSONArray arry = JSONArray.parseArray(data);
        //    JSONArray ret = new JSONArray();
        //    for (Object next : arry) {
        //        ret.add(removeSeq(next));
        //    }
        //    return ret;
        //}
        //return jsonData;
        if (jsonData instanceof Map) {
            JSONObject itemobj = new JSONObject();
            JSONObject handlerData = (JSONObject) jsonData;
            handlerData.forEach((k, v) -> itemobj.put(k, removeSeq(v)));
            String type = itemobj.getString("type");
            if (StrUtil.isNotEmpty(type) && ("O".equals(type) || "R".equals(type))) {
                itemobj.remove("seq");
            }
            return itemobj;
        } else if (jsonData instanceof List) {
            JSONArray arry = (JSONArray) jsonData;
            JSONArray ret = new JSONArray();
            for (Object next : arry) {
                ret.add(removeSeq(next));
            }
            return ret;
        }
        return jsonData;
    }

    boolean checkIpExisted(String ip, JSONObject project);

    JSONObject getEnumExtParams(String org_id, Integer status);

    List<JSONObject> listExtparam(JSONObject query);

    List<JSONObject> getDeliverExtParams(String deliver_id, String project_id);

    void cacheWxworkSessionInfo(String session_id, JSONObject data, long timeout);

    JSONObject getWxSessionInfo(String session_id);

    void checkQrDeliverDisable(Integer source, JSONObject params);

    void checkDedicatedDeliverDelete(Integer source, JSONObject params);

    /**
     * 唯一性链接检查
     *
     * @param project_id   问卷id
     * @param new_seq      seq
     * @param all_params   答题链接参数
     * @param clear_answer 清除答案
     * @return java.lang.Integer
     * @author chenglong.yue <chenglong.yue@idiaoyan.com>
     * @date 2023/5/11 9:54
     */
    Integer checkUniqLink(String project_id, Integer new_seq, JSONObject all_params, Boolean clear_answer);

    Integer getNextRspdSeq(Integer count);

    void clearAnswersBySeq(String projectId, Integer seq);

    Integer deleteAttachment(String pid, Integer seq, String id);

    List<SurveyColumnVo> getColumnsNeDelete(String projectId, JSONObject args, String orgId);

    void checkDeliverEnableRespondent(String deliverId);

    ProjectDto post4SurvyCommon(Integer source, String projectId, HttpServletRequest request, String group_code);

    void mqSurveyRespondentModify(SurveyRespondent rspd, JSONObject total_answer, JSONObject custom_answer);

    void memberFootPrintMq(SurveyRespondent rspd, JSONObject total_answer, JSONObject custom_answer);

    Pair<String, String> getFirstSatisfActionNpsGid(QstructsDto qstruct, List<String> satisfactionGids, List<String> npsGids);

    List<Question> getScoreQuestions(String qstruct_id);

    JSONObject getRespondentMqData(SurveyRespondent rspd, JSONObject total_answer, JSONObject custom_answer, Boolean is_copy);

    void finishProject(String project_id) throws Exception;

    String getCorpUrl();

    JSONObject getWxInfoAndWxSessionIdBySid(String sid);

    JSONObject getWxInfoBySessionId(String wxSessionId);

    JSONObject getUserInfoBySessionId(String sid);

    void cacheWxuserData(String session_id, JSONObject data, Integer timeout);

    void checkDeliverParams(JSONObject allParams);

    String getDeliverSecret();

    String getSurveySecret();

    void projectVersionCheck(ProjectDto project, Integer version);

    void projectVersionCheckPreview(ProjectDto project, Integer version);

    /**
     * 验证问卷是否超过答题时限
     */
    void queryTimeV2(ProjectDto project, SurveyRespondent rspd, Integer seq);

    void redisDataCleanBySeq(String projectId, Integer seq);

    void saveRspdProp(String projectId, Integer seq, JSONObject answers, JSONObject others, JSONObject otherOpen, LocalDateTime beginTime, Integer status);

    LocalDateTime getRspdCreated(Integer seq);

    JSONObject decryptExtparamters(JSONObject customStoreTemp, ProjectDto project);

    void checkExtParamsUniqueness(String deliverId, String projectId, Integer seq, JSONObject customStore, int xmCustom);

    void wxCheck(Integer source, String openId, String appId, ProjectDto project);

    List<Integer> qdesGetQlistWithPid(String projectId, boolean isPreview);

    JSONObject getRspdWxInfo(Integer seq);

    Integer getRspdStatus(Integer seq);

    JSONObject getAnsweredAnswers(String pid, Integer seq);

    JSONObject getAnsweredData(Integer seq);

    JSONArray getLastAnsweredPageQidlist(Integer seq);

    Object getNewQuestionOrder(String project_id, Integer seq);

    JSONArray getSortsBySeq(Integer seq);

    JSONArray getRspdGntList(Integer seq);

    void checkQuestionNameList(JSONObject params);

    List<String> saveUniqAnswers(String project_id, Integer seq, JSONObject answers, JSONArray uniq_check_list, boolean history);

    void getCustomData(String project_id, JSONArray custom_data_action);

    void savePageAnswers(String pid, Integer seq, Object answer_str, String page_id, JSONArray page_qid_list, Integer version, boolean goback_flag, Object custom_answers, JSONArray question_list_input);

    String getQuotaCheckpointsForPageAnswers(String project_id, JSONObject page_answers);

    int getClientType(String user_agent);

    Pair<Boolean, List<SurveyQuotaCheckItemVo>> isQuotafull(String project_id, Integer seq, JSONObject answers_dict, String quota_id);

    String getGroupIdOnSurvey(String group_code, ProjectDto project);

    Pair<SurveyRespondent, Boolean> submitRespondent(JSONObject param);

    void surveyUnionCallback(String idy_sid, Integer rid, String q);

    void saveDeliverUniqueAnswer(String deliver_id, Integer seq, JSONObject custom_answers);

    void setRspdStatus(Integer seq, Integer status);

    SurveyRespondentData getOneAndUpdate4RespondentData(LambdaQueryWrapper<SurveyRespondentData> queryWrapper, JSONObject update_params, Boolean is_upsert);

    SurveyRespondentData createRespondentdata(JSONObject param);

    SurveyRespondentData createRelRespondentdata(JSONObject data, JSONObject sub_data);

    void createRelRespondenthashquestionlist(JSONObject data);

    Long getNextSubmitSeq(String project_id, Integer version);

    Long getMaxtSubmitSeq(String project_id, Integer version);

    String getRspdDlvrRid(Integer seq);

    String getRspdIdySid(Integer seq);

    String getRspdDedCode(Integer seq);

    Integer checkAutoFilter(String project_id, JSONObject answers, Integer source, String group_id, JSONObject custom_answers);

    SurveyRespondent discardAutoFilter(SurveyRespondent rspd);

    void triggerRspdStatus(SurveyRespondent rspd, String action, String project_id) throws Exception;

    void mqSurveyRespondentStatusChanged(SurveyRespondent rspd, Integer old_status);

    void saveRespondentpropDelay(SurveyRespondent respondent, String project_id, Integer seq, JSONObject total_answer, String deliver_id, String deliver_group_id, String member_id, JSONObject custom_answer);

    void mqSurveyRespondentSubmit(SurveyRespondent rspd, JSONObject total_answer, JSONObject custom_answer, Boolean is_copy);

    void saveRspdPropTaskWrapper(String project_id, Integer seq, SurveyRespondent rspd, JSONObject answers, JSONObject others, JSONObject other_open, Integer status, LocalDateTime begin_time);

    boolean checkQrDeliverDisableV2(Integer source, JSONObject allParams);

    void cacheWxSessionInfo(String session_id, JSONObject data);

    JSONObject getWxworkUserDetail(String session_id);

    void setRspdWxInfo(Integer seq, JSONObject wxUserInfo);

    JSONObject respondentCountAutoInc(String projectId, Map<String, Long> kwargs);

    <T> Pair<String, File> exportWithDynamicData(String fileName, List<List<List<String>>> head, List<List<List<T>>> exportData, List<String> sheetNames, HttpServletResponse response, String ossPath);

}
